// $Id: nodemesh.cpp 74 2005-05-15 19:54:19Z hansemil $
#include "nodemesh.h"
#include "filemeshbit.h"
#include "filemeshquadstate.h"
#include <iostream>
#include <typeinfo>

// The weighting of the Freenet routing source and the File Mesh routing, sum SHOULD always be 1
#define ROUTE_WEIGHT_FREENET 0.0
#define ROUTE_WEIGHT_MESH 1.0

/**
 * Init all members.
 *
 * @param bandwidthUp Upload bandwidth in bytes per timestep (1 time step is in
 *  seconds and can be obtained from Clock::m_secondsPerTimeStep)
 * @param bandwidthDown Download bandwidth in bytes per timestep.
 * @param routingTableSize Number of entries in the routing table.
 * @param fileStoreSize Number of files to fit in the file store.
 * @param requestHopsToLive The HTL to use on requests.
 * @param insertHopsToLive The HTL to use on inserts.
 * @param requestProbability The probability for one node to request a file at
 *  one time step.
 * @param insertProbability The probability for one node to insert a file at
 *  one time step.
 * @param fileHandler A pointer to the file handler to use.
 */
NodeMesh::NodeMesh(unsigned int bandwidthUp, unsigned int bandwidthDown, unsigned int routingTableSize, unsigned int fileStoreSize, unsigned int requestHopsToLive, unsigned int insertHopsToLive, float requestProbability, float insertProbability, FileHandler *fileHandler)
	: Node(bandwidthUp, bandwidthDown, routingTableSize, fileStoreSize, requestHopsToLive, insertHopsToLive, requestProbability, insertProbability, fileHandler), NodeVanilla(bandwidthUp, bandwidthDown, routingTableSize, fileStoreSize, requestHopsToLive, insertHopsToLive, requestProbability, insertProbability, fileHandler)
{
	// Create the fileMesh of the desired FileMesh-type
	m_fileMesh = new FileMeshBit();
	//m_fileMesh = new FileMeshQuadState();
}

/**
 * Free all memory that we claimed.
 */
NodeMesh::~NodeMesh()
{
	// Remove the fileMesh
	delete m_fileMesh;

	// Remove the fileMeshList content
	for(map<Node *, FileMesh *>::iterator fileMesh = m_fileMeshList.begin(); fileMesh != m_fileMeshList.end(); fileMesh++){
		delete (*fileMesh).second;
	}
	m_fileMeshList.clear();
}

/**
 * This method searches for a file in the network by reqursing through its
 * neighbor nodes requestFile-methods.
 *
 * @param requestor A pointer to the requesting node.
 * @param file A pointer to the file to request.
 * @param hopsToLive Freenet style hops to live counter. This counter is
 *  decreased at each node and the request is failed if no file has been found
 *  when the counter has reached zero.
 * @returns True is returned if the file is found on this node (or another) and
 *  a transfer will be set up. False is returned only if all neighbors fail to
 *  locate the file.
 */
bool NodeMesh::routeFile(Node *requestor, NodeRequestFileParameters *params)
{

	(dynamic_cast<NodeMesh *>(requestor))->setFileMesh(this, m_fileMesh);
	/// @todo Experiment with setting a mesh in the other direction (upstreams) too. This would require some re-coding but might proove quite cool?
	return NodeVanilla::routeFile(requestor, params);
}

/**
 * Store a file on this node. If the file store is full it will delete the
 * Least Recently Used file in the filestore. It also updates the file mesh
 * that we keep.
 *
 * @param file The file to store
 * @param fullSize Do we wish to store the file with its full size (or create
 *  an empty file (false))? Default is false = empty file.
 * @returns A pointer to the newly created StoredFile object
 */
StoredFile *NodeMesh::storeFile(File *file, bool fullSize)
{
	StoredFile *result;
	bool fileStoreFull = false;

	if (m_fileStore.size() >= m_fileStoreSize) {
		fileStoreFull = true;
	}

	result = NodeVanilla::storeFile(file, fullSize);

	if (fileStoreFull) {
		// We need to rebuild the filter since a file was removed
		m_fileMesh->rebuild(m_fileStore);
	}
	else {
		// Just update the filter
		m_fileMesh->update(result->m_file->m_hash, m_fileStore);
	}

	return result;
}

/**
 * Set the file mesh (FileMesh) of this node
 * @param node The updating node
 * @param filter A pointer to the updating nodes file mesh. Please note that in all sensible cases a COPY of the mesh should be stored, not the pointer!
 */
void NodeMesh::setFileMesh(Node *node, FileMesh *filter)
{
	// Look for an earlier filter for this node
	map<Node *, FileMesh *>::iterator position=m_fileMeshList.find(node);
	if (position == m_fileMeshList.end()) {
		// We did not have a filter for this node yet
		// Create a copy of the file mesh and save it
		m_fileMeshList[node] = filter->newCopy();
	}
	else {
		// We need to update the filter. Remove old and replace!
		delete (*position).second;
		// Create a copy of the file mesh and save it
		(*position).second = filter->newCopy();
	}
}

/**
 * Generates a request order list weighted by file mesh. It is used by the
 * requestFile method from the NodeVanilla class.
 */
void NodeMesh::generateRequestOrderList(File *file, list<KeyDistanceHelper> &list)
{
	unsigned int distance;

	for (std::list<RoutingEntry>::iterator entry=m_routingTable.begin(); entry!=m_routingTable.end(); entry++) {
		// Calc freenet style distance
		distance = ::abs((int) (*entry).m_hash - file->m_hash);

		// Add mesh weight
		map<Node *, FileMesh *>::iterator fileMesh = m_fileMeshList.find((*entry).m_node);
		if (fileMesh != m_fileMeshList.end()) {
// cout << "Freenet says:\t" << distance << endl;	// DEBUG
			distance = (unsigned int)(distance*ROUTE_WEIGHT_FREENET);
			distance += (unsigned int)(((*fileMesh).second->calcDistance(file->m_hash))*ROUTE_WEIGHT_MESH);
		}

		list.push_back(KeyDistanceHelper(distance, &(*entry)));
	}
	list.sort(SortKeyDistanceHelper());
}

/**
 * @return The number of file mesh list entries.
 */
int NodeMesh::getFileMeshCount()
{
   return m_fileMeshList.size();
}
